﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Xml;
using System.Linq;
using System.IO;
using System.Xml.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace ToolKitIESiteList.Library
{

    public static partial class EmieDocument
    {
        public static event EventHandler<ProgressEventArgs> ProgressChange;
        public static void OnProgressChange(ProgressEventArgs e)
        {
            ProgressChange?.Invoke(null, e);
        }
        public static int VersionEmieDocument { get; private set; }
        public static IReadOnlyCollection<IEModeDataItem> DuplicateItems { get; private set; }
        public static string DateCreatedEmieDocument { get; private set; }
        public static string ToolEmieDocument { get; private set; }
        public static MemoryStream TempEmieDocument { get; set; }
        public static string FileNameEmieDocument { get; set; }
        private static List<IEModeDataItem> DataItems { get; set; }
        public static IEnumerable<IEModeDataItem> GetSitesByDomain(string domain)
        {
            return DataItems
                .Where(d => d.HostPort == domain);

        }
        public static IEnumerable<IEModeDataItem> AddSitesToPublicDomain(IEnumerable<IEModeDataItem> localdomains, string publicdomain, bool removePort)
        {
            var result = new List<IEModeDataItem>();
            foreach (var item in localdomains.ToArray())
            {
                var hostport = item.HostPort;
                if (!removePort)
                {
                    hostport = item.Uri.Host;
                }
                hostport = hostport.Replace(item.HostPort, publicdomain);
                var newUri = new Uri(HelperEmie.AddScheme(string.Format("{0}{1}", hostport, item.AbsolutePath)));
                var newitem = new IEModeDataItem
                {
                    AbsolutePath = item.AbsolutePath,
                    HostPort = hostport,
                    Comment = item.Comment,
                    CompatMode = item.CompatMode,
                    IsLogicalDuplicate = item.IsLogicalDuplicate,
                    OpenIn = item.OpenIn,
                    OriCompatMode = item.OriCompatMode,
                    OriOpenIn = item.OriOpenIn,
                    Uri = newUri
                };
                result.Add(newitem);
                DataItems.Add(newitem);
            }
            return result;
        }
        static EmieDocument()
        {
            ResetEnvironment();
        }
        [SuppressMessage("Microsoft.Design", "CA1006")]
        public static Dictionary<string, List<IEModeDataItem>> Sanitize(IEnumerable<IEModeDataItem> items)
        {
            var result = new Dictionary<string, List<IEModeDataItem>>();
            if (items.Count() == 1)
            {
                return result;
            }
            var internalItems = items.OrderBy(i => i.AbsolutePath);
            IEModeDataItem root = internalItems.First();
            //level root
            if (internalItems.First().AbsolutePath == "/")
            {
                var RootMode = root.CompatMode;
                var RootOpen = root.OpenIn;
                if (!internalItems.Skip(1).Any(i => RootMode != i.CompatMode || RootOpen != i.OpenIn))
                {
                    if (!result.ContainsKey(root.HostPort))
                    {
                        result.Add(root.HostPort, new List<IEModeDataItem>());
                    }
                    result[root.HostPort].AddRange(internalItems.Skip(1));
                }
            }
            return result;
        }
        public static string[] GetDomains()
        {
            return GetDomains(null, true);
        }
        public static string[] GetDomains(string domain, bool supressPort)
        {
            if (string.IsNullOrEmpty(domain))
            {
                return DataItems
                    .Select(s => s.HostPort)
                    .Distinct()
                    .ToArray();
            }
            if (supressPort)
            {
                return DataItems
                    .Where(s => s.Uri.Host == domain)
                    .Select(s => s.Uri.Host)
                    .Distinct()
                    .ToArray();
            }
            return DataItems
                .Where(s => s.HostPort == domain)
                .Select(s => s.HostPort)
                .Distinct()
                .ToArray();

        }
        public static IEnumerable<IEModeDataItem> GetItemsByDomains(string[] domains)
        {
            return DataItems
                .Where(s => domains.AsEnumerable().Any(d => d == s.HostPort));
        }
        public static int RemoveDomains(IEnumerable<IEModeDataItem> toDelete)
        {
            var qtd = 0;
            foreach (var item in toDelete.ToArray())
            {
                if (DataItems.Remove(item))
                {
                    qtd++;
                }
            }
            return qtd;
        }
        public static void AddOrUpdateDataItem(string oldurl, string newurl, CompatMode mode, OpenIn open, string comment)
        {
            var posold = FindIndexUri(oldurl);
            var posnew = FindIndexUri(newurl);
            if (posnew < 0)
            {
                if (posold >= 0)
                {
                    DataItems.RemoveAt(posold);
                }
                var newUri = new Uri(HelperEmie.AddScheme(newurl));
                DataItems.Add(new IEModeDataItem
                {
                    HostPort = newUri.GetHostAndPort(),
                    AbsolutePath = newUri.AbsolutePath,
                    Comment = comment,
                    CompatMode = mode,
                    IsLogicalDuplicate = false,
                    OpenIn = open,
                    OriCompatMode = mode,
                    OriOpenIn = open,
                    Uri = newUri
                });
                return;
            }
            DataItems[posold].Comment = comment;
            DataItems[posold].CompatMode = mode;
            DataItems[posold].OpenIn = open;
            DataItems[posold].OriCompatMode = mode;
            DataItems[posold].OriOpenIn = open;
        }
        public static Dictionary<CompatMode, int> StatsCompatMode
        {
            get
            {
                Dictionary<CompatMode, int> result =
                    DataItems
                    .GroupBy(o => o.CompatMode)
                    .ToDictionary(g => g.Key, g => g.Count());
                return result;
            }
        }
        public static Dictionary<OpenIn, int> StatsOpenIn
        {
            get
            {
                Dictionary<OpenIn, int> result =
                    DataItems
                    .GroupBy(o => o.OpenIn)
                    .ToDictionary(g => g.Key, g => g.Count());
                return result;
            }
        }
        public static void UpdateVersionEmieDocument(int value)
        {
            VersionEmieDocument = value;
        }
        public static void UpdateDateCreatedEmieDocument()
        {
            UpdateDateCreatedEmieDocument(DateTime.Now.ToString());
        }
        public static void UpdateDateCreatedEmieDocument(string value)
        {
            DateCreatedEmieDocument = value;
        }
        public static void ClearTempEmieDocument()
        {
            if (TempEmieDocument != null)
            {
                TempEmieDocument.Close();
            }
            TempEmieDocument = null;
        }
        public static void ResetEnvironment()
        {
            FileNameEmieDocument = null;
            DataItems = new List<IEModeDataItem>();
            DuplicateItems = new List<IEModeDataItem>();
            VersionEmieDocument = 1;
            DateCreatedEmieDocument = DateTime.Now.ToString();
            ToolEmieDocument = "EMIESiteListManager";
            if (TempEmieDocument != null)
            {
                TempEmieDocument.Close();
            }
            TempEmieDocument = null;
        }
        public static ResultMerge OpenStreamV2EmieDocument(Stream fileName, bool removep80, string[] excludeExtension, string[] excludeHost)
        {
            if (fileName == null)
            {
                throw new ArgumentException("Null value", "fileName");
            }
            fileName.Position = 0;
            XmlDocument XmlFile = new XmlDocument();
            XmlFile.Load(fileName);
            return ReadEmieDocumentV2(XmlFile, removep80, excludeExtension,excludeHost);
        }
        public static ResultMerge OpenFileV2EmieDocument(string fileName, bool removep80,string[] excludeExtension, string[] excludeHost)
        {
            XmlDocument XmlFile = new XmlDocument();
            XmlFile.Load(fileName);
            return ReadEmieDocumentV2(XmlFile, removep80, excludeExtension, excludeHost);
        }
        private static ResultMerge ReadEmieDocumentV2(XmlDocument XmlFile, bool removep80,string [] excludeExtension, string[] excludeHost)
        {
            var newItems = new List<IEModeDataItem>();
            var dupItems = new List<IEModeDataItem>();
            var p80Items = new List<IEModeDataItem>();
            var delExt = new List<IEModeDataItem>();
            var delHost = new List<IEModeDataItem>();

            DateCreatedEmieDocument = XmlFile.GetDateCreated();
            VersionEmieDocument = int.Parse(XmlFile.GetVersion());
            var pos = 0;
            var allsistes = XmlFile.GetAllSites();
            var max = allsistes.Count;
            foreach (XmlElement item in allsistes)
            {
                var xmlUrl = item.GetUrl();
                var tmpuri = new Uri(HelperEmie.AddScheme(xmlUrl));
                var ext = Path.GetExtension(tmpuri.Segments[tmpuri.Segments.Length-1]);
                if (excludeHost.Any(h => h.ToUpperInvariant() == tmpuri.Host.ToUpperInvariant()))
                {
                    var hp = tmpuri.GetHostAndPort();
                    var cm = item.GetCompatMode();
                    var op = item.GetOpenIn();
                    delHost.Add(new IEModeDataItem
                    {
                        Uri = tmpuri,
                        HostPort = hp,
                        AbsolutePath = tmpuri.AbsolutePath,
                        Comment = item.GetComment(),
                        CompatMode = cm,
                        OpenIn = op,
                        OriCompatMode = cm,
                        OriOpenIn = op
                    });
                    pos++;
                    OnProgressChange(new ProgressEventArgs { value = pos, Maxvalue = max });
                    continue;
                }
                if (excludeExtension.Any(e => e.ToUpperInvariant() == ext.ToUpperInvariant()))
                {
                    var hp = tmpuri.GetHostAndPort();
                    var cm = item.GetCompatMode();
                    var op = item.GetOpenIn();
                    delExt.Add(new IEModeDataItem
                    {
                        Uri = tmpuri,
                        HostPort = hp,
                        AbsolutePath = tmpuri.AbsolutePath,
                        Comment = item.GetComment(),
                        CompatMode = cm,
                        OpenIn = op,
                        OriCompatMode = cm,
                        OriOpenIn = op
                    });
                    pos++;
                    OnProgressChange(new ProgressEventArgs { value = pos, Maxvalue = max });
                    continue;
                }

                if (removep80)
                {
                    var hp = tmpuri.GetHostAndPort();
                    if (hp.EndsWith(":80"))
                    {
                        var cm = item.GetCompatMode();
                        var op = item.GetOpenIn();
                        p80Items.Add(new IEModeDataItem
                        {
                            Uri = tmpuri,
                            HostPort = hp,
                            AbsolutePath = tmpuri.AbsolutePath,
                            Comment = item.GetComment(),
                            CompatMode = cm,
                            OpenIn = op,
                            OriCompatMode = cm,
                            OriOpenIn = op
                        });
                        xmlUrl = string.Format("{0}{1}", hp.Replace(":80", ""), tmpuri.AbsolutePath);
                        tmpuri = new Uri(HelperEmie.AddScheme(xmlUrl));
                    }
                }
                var itemcm = item.GetCompatMode();
                var itemop = item.GetOpenIn();
                var ieItem = new IEModeDataItem
                {
                    Uri = tmpuri,
                    HostPort = tmpuri.GetHostAndPort(),
                    AbsolutePath = tmpuri.AbsolutePath,
                    Comment = item.GetComment(),
                    CompatMode = itemcm,
                    OpenIn = itemop,
                    OriCompatMode = itemcm,
                    OriOpenIn = itemop
                };
                var duploc = "new";
                var exist = newItems.FindIndex(i => i.Url.ToLower() == ieItem.Url.ToLower());
                if (exist < 0)
                {
                    duploc = "dat";
                    exist = DataItems.FindIndex(i => i.Url.ToLower() == ieItem.Url.ToLower());
                }
                if (exist < 0)
                {
                    newItems.Add(ieItem);
                }
                else
                {
                    var IsEqual = false;
                    var curmode = ieItem.CompatMode;
                    var curopen = ieItem.OpenIn;
                    if (duploc == "new")
                    {
                        curmode = newItems[exist].CompatMode;
                        curopen = newItems[exist].OpenIn;
                        if (ieItem.CompatMode == newItems[exist].CompatMode)
                        {
                            if (ieItem.OpenIn == newItems[exist].OpenIn)
                            {
                                IsEqual = true;
                            }
                        }
                    }
                    else if (duploc == "dat")
                    {
                        curmode = DataItems[exist].CompatMode;
                        curopen = DataItems[exist].OpenIn;
                        if (ieItem.CompatMode == DataItems[exist].CompatMode)
                        {
                            if (ieItem.OpenIn == DataItems[exist].OpenIn)
                            {
                                IsEqual = true;
                            }
                        }
                    }
                    ieItem.OriOpenIn = curopen;
                    ieItem.OriCompatMode = curmode;
                    ieItem.IsLogicalDuplicate = IsEqual;
                    dupItems.Add(ieItem);
                }
                pos++;
                OnProgressChange(new ProgressEventArgs { value = pos, Maxvalue = max });
            };
            DataItems.AddRange(newItems.ToArray());
            return new ResultMerge(newItems.ToArray(), dupItems.ToArray(),p80Items.ToArray(), delExt.ToArray(),delHost.ToArray());
        }
        public static int CountSiteDuplicate()
        {
            return DuplicateItems.Distinct(new IEModeDataItemUriComparer()).Count();
        }
        public static int CountSites()
        {
            return DataItems.Count;
        }
        public static IEnumerable<IEModeDataItem> GetDataItems()
        {
            return GetDataItems(FilterType.None, null);
        }
        [SuppressMessage("Microsoft.Maintainability", "CA1502")]
        public static IEnumerable<IEModeDataItem> GetDataItems(FilterType filter, string value)
        {
            switch (filter)
            {
                case FilterType.None:
                    return DataItems;
                case FilterType.CompatMode:
                    return DataItems.Where(d => d.CompatMode == (CompatMode)Enum.Parse(typeof(CompatMode), value));
                case FilterType.OpenIn:
                    return DataItems.Where(d => d.OpenIn == (OpenIn)Enum.Parse(typeof(OpenIn), value));
                case FilterType.Duplicate:
                    return DataItems.Where(s => DuplicateItems.Select(d => d.Url).Contains(s.Url));
                case FilterType.Port80:
                    return DataItems.Where(g => g.HostPort.EndsWith(":80"));
                case FilterType.Custom:
                    {
                        if (value == "UrlRootIE11")
                        {
                            return DataItems.Where(g => g.AbsolutePath == "/" && (g.CompatMode == CompatMode.IE11 || g.CompatMode == CompatMode.Default));
                        }
                        else if (value == "Slash")
                        {
                            return DataItems.Where(e => e.AbsolutePath.EndsWith("/") && e.AbsolutePath.Length > 1);
                        }
                        else if (value == "SingleUrl")
                        {
                            var hosts = DataItems.GroupBy(g => g.HostPort)
                                .Where(g => g.Count() == 1)
                                .Select(g => g.Key).ToArray();
                            var result = new ConcurrentBag<IEModeDataItem>();
                            Parallel.ForEach(DataItems, item =>
                            {
                                if (hosts.Contains(item.HostPort))
                                {
                                    result.Add(item);
                                }
                            });
                            return result.ToList().AsEnumerable();
                        }
                    }
                    break;
            }
            return new List<IEModeDataItem>();
        }
        public static void FindDuplicateSite()
        {
            var urls = DataItems.GroupBy(g => g.Url)
                .Where(g => g.Count() > 1)
                .Select(g => g.Key);
            if (urls.Count() == 0)
            {
                DuplicateItems = new List<IEModeDataItem>();
            }
            else
            {
                DuplicateItems = DataItems
                    .Where(s => urls.Contains(s.Url)).ToList();
            }
        }
        public static IReadOnlyList<IEModeDataItem> FindSiteSpecificPort80()
        {
            return DataItems
                .Where(g => g.HostPort.EndsWith(":80"))
                .ToList();
        }
        public static int CountPort80()
        {
            return DataItems
                .Where(g => g.HostPort.EndsWith(":80"))
                .Count();
        }
        public static bool RemoveSpecificPort80()
        {
            var result = false;
            Parallel.ForEach(DataItems.Where(e => e.HostPort.EndsWith(":80")), item =>
            {
                result = true;
                item.Uri = new Uri(HelperEmie.AddScheme(string.Format("{0}{1}", item.HostPort.Replace(":80", ""), item.AbsolutePath)));
                item.HostPort = item.Uri.GetHostAndPort();
                item.AbsolutePath = item.Uri.AbsolutePath;
            });
            return result;
        }
        [SuppressMessage("Microsoft.Design", "CA1054")]
        public static int FindIndexUri(string url)
        {
            if (string.IsNullOrEmpty(url)) return -1;
            return DataItems.FindIndex(i => i.Url.ToLower() == url.ToLower());
        }
        public static int CountUri(Uri uri)
        {
            return DataItems.Count(i => i.Uri == uri);
        }
        [SuppressMessage("Microsoft.Design", "CA1054")]
        public static void OverWriterCompatMode(CompatMode newMode, string url)
        {
            UpdateCompatMode(newMode, FindIndexUri(url));
        }
        [SuppressMessage("Microsoft.Design", "CA1054")]
        public static void OverWriterOpenIn(OpenIn newOpen, string url)
        {
            UpdateOpenIn(newOpen, FindIndexUri(url));
        }
        public static void UpdateCompatMode(IEModeDataItem item)
        {
            var index = DataItems.FindIndex(e => e == item);
            UpdateCompatMode(item.CompatMode, index);
        }
        private static void UpdateCompatMode(CompatMode newmode, int index)
        {
            DataItems[index].CompatMode = newmode;
            DataItems[index].OriCompatMode = newmode;
        }
        public static void UpdateOpenIn(IEModeDataItem item)
        {
            var index = DataItems.FindIndex(e => e == item);
            UpdateOpenIn(item.OpenIn, index);
        }
        private static void UpdateOpenIn(OpenIn newopen, int index)
        {
            DataItems[index].OpenIn = newopen;
            DataItems[index].OriOpenIn = newopen;
        }
        public static int RemoveSites(IEnumerable<IEModeDataItem> sitesToRemove)
        {
            var qtd = 0;
            foreach (var item in sitesToRemove)
            {
                var index = DataItems.FindIndex(e => e == item);
                if (index >= 0)
                {
                    DataItems.RemoveAt(index);
                    qtd++;
                }
            }
            return qtd;
        }
    }
}
